package com.onpositive.wikistorage;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;

import storage.v1.AbstractStorageWithRegistry;
import storage.v1.StorageWithRegistry;


public class PictureManager {
	
private static final String PIC_DATA_SUBPATH = "picdata/";
//	public static final String PICTURE_LINKS_ARRAY_PROPERY_NAME = "Picture links" ;
	public static final String WIKIPEDIA_FILE_PATH = "http://en.wikipedia.org/wiki/File:" ;
	public static final String picturesSubpath = "pictures/" ;
	public static final String validPicturesList = "valid-pics.txt" ;	
	
	static private HashMap<String,PictureManager> map = new HashMap<String, PictureManager>();
	
	HashMap<String,String> validPics = null ;	
	public static PictureManager getInstance( String rootPath ){
		
		PictureManager instance = map.get(rootPath) ;
		if( instance == null )
		{
			instance = new PictureManager(rootPath) ;
			map.put(rootPath, instance) ;
		}
		return instance ;
	}	
	
	private PictureManager( String rootPath ){
		this.rootPath = constructPicturePath(rootPath);
		new File( this.rootPath ).mkdirs() ;
		initValidPicsSet() ;
	}
	
	public static String constructPicturePath(String rootPath) {
		return rootPath + ( rootPath.endsWith("/")||rootPath.endsWith("\\") ? "" : "/" ) + picturesSubpath ;
	}
	String rootPath ;	
	
	public boolean checkPicturePresence( String wikiPath, String docId )
	{
		String str0 = convertToLocalPath( wikiPath, docId ) ;
		String s = validPics.get(str0) ;
		if( s != null ){
			Boolean val = Boolean.parseBoolean(s);
			if( val != null )
				return val ;
		}
				
		boolean val = checkPictureLocalPresence( str0 );
		validPics.put(str0, Boolean.toString(val) ) ; 
		return val ;
	}
	
	private void initValidPicsSet()
	{
		validPics = new HashMap<String, String>() ;
		String pth = this.rootPath + validPicturesList ;
		try {
			BufferedReader bReader = new BufferedReader( new InputStreamReader( new FileInputStream( pth ) )  ) ;

			String line = bReader.readLine(), val ;
			while( line != null )
			{
				val = bReader.readLine() ;
				validPics.put(line,val) ;
				line = bReader.readLine() ;
			}
		} catch (FileNotFoundException e) {
			//e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}		
	}
	
	public void saveValidPicsList()
	{
		String pth = this.rootPath + validPicturesList ;
		try {
			
			StringBuilder builder = new StringBuilder() ;
			for( Map.Entry<String,String> entry : validPics.entrySet() )
			{
				builder.append( entry.getKey() ) ;
				builder.append("\n") ;
				builder.append( entry.getValue() ) ;
				builder.append("\n") ;
			}			
			OutputStreamWriter wrt = new OutputStreamWriter( new FileOutputStream(pth) ) ;
			wrt.write(builder.substring( 0, builder.length()-1 ) ) ;
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}		
	}

	public ArrayList<byte[]> getPictures( Collection<String> wikiPaths, String docId )
	{	
		ArrayList<byte[]> result = new ArrayList<byte[]>() ;
		
		for( String s : wikiPaths )
		{
			byte[] pic = getPicture( s, docId) ;
			if( pic != null )
				result.add( pic ) ;
		}
		
		return result ;
	}
	
	public byte[] getPicture( String wikiPath , String docId )
	{
		String localPath = convertToLocalPath( wikiPath, docId ) ;
		File f = new File( localPath ) ;
		if( f.exists() )
		{
			try {
				int l = (int)f.length() ;
				byte[] result = new byte[l] ;
				FileInputStream fis = new FileInputStream( f ) ;			
				fis.read( result , 0 , l ) ;
				
				return result ;
			
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		else
			return grabPictureFromNet(wikiPath, docId) ;
		
		return null ;
	}
	
	public void grabPicturesFromNet( Collection<String> wikiPaths , String docId )
	{
		for( String s : wikiPaths ){
			grabPictureFromNet( s , docId) ;
			System.out.print( docId + ": " + s + "\n") ;
		}
	}
	
	public byte[] grabPictureFromNet( String wikiPath , String docId )
	{
		if(wikiPath.startsWith("http:"))
			return null ;
		String localPath = convertToLocalPath(wikiPath, docId) ;
		new File( this.rootPath + docId + "/" ).mkdirs() ;			
		File f = new File( localPath ) ;
		
		if( f.exists() )
			return null;		
		
		try {
			
			String link = constructURL(wikiPath) ;
			if( link == null )
				return null ;
			
			byte[] pic = loadPicture(link) ;			
			
			FileOutputStream fos = new FileOutputStream( f ) ;
			try{
				fos.write( pic ) ;
				fos.close() ;
			}catch(NullPointerException e){
				e.printStackTrace() ;
			}
			return pic ;
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null ;
	}
	
	public String convertToLocalPath( String s, String docId) {
		return rootPath + docId +"/" + s;
	}
	
	private boolean checkPictureLocalPresence( String path ){
		
		if(  path == null ||  path.length() == 0 )
			return false ;
 
		Image image = null ;
		try{
			image = new Image( Display.getDefault(),  path );
		}
		catch(Exception e){
			return false ;			
		}
		if( image == null || image.isDisposed() )
		{
			return false ;
		}
		else{
			image.dispose() ;
			return true ;
		}		
	}

	private byte[] loadPicture( String link  )
	{
		if( link == null )
			return null ;
		
		try {
							
			URL url = new URL( link );

			BufferedInputStream picStream = new BufferedInputStream( url.openStream() ) ;
				
			ByteArrayOutputStream os = new ByteArrayOutputStream() ;
				
			for( int n = picStream.read() ; n != -1 ; n = picStream.read() )
				os.write((byte)n);
			
			os.close();
				
			return os.toByteArray() ;
		} catch (IOException e) {
			System.out.print( "cannot find picture: " ) ;
			System.out.print( link ) ;
			System.out.print( "\n" ) ;
		}
		return null ;
	}
	
	
	StorageWithRegistry docDirStorage ;
	StorageWithRegistry picFileStorage ;
	
	private void initDirAndFileStorages(){
		
		if( docDirStorage == null )
			docDirStorage = new StorageWithRegistry( rootPath, "doc-dir" ) ;
		
		docDirStorage.initMap() ;
		
		if( picFileStorage == null )
			picFileStorage = new StorageWithRegistry( rootPath, "doc-dir" ) ;
		
		picFileStorage.initMap() ;
	}
	
	public String constructURL( String s ) throws IOException
	{
//		if( s.startsWith("http://")  )
//			return s ;
		if( s.startsWith("http:") )
			return s ;
		
		if( !isPic(s) )
			return null ;
		String str = s.replace(" ", "_") ;
		String str0 = URLEncoder.encode(str, "UTF-8") ; 
		
		//str = str.replace("&", "%26") ;
		str = str0 ;//performUrlEncoding( str0 ) ;
		
		URL url = new URL( WIKIPEDIA_FILE_PATH + str);		
		
		String strLC = str.toLowerCase() ;
		String picDataStringContent = getPicDataContent(url) ;
		if( picDataStringContent == null )
			return null ;
		String contentLC = picDataStringContent.toLowerCase() ;
		int endIndex = 0 ;
		int beginIndex = -1 ;
		
		try{
			for(  ; beginIndex == -1 ;){
				endIndex = contentLC.indexOf( strLC , endIndex ) ;
				String tmp = contentLC.substring(endIndex) ;
				String tmp2 = contentLC.substring(0,endIndex) ;
				beginIndex = contentLC.lastIndexOf("//upload.wikimedia.org/wikipedia/", endIndex) ;
				if( endIndex - beginIndex - "//upload.wikimedia.org/wikipedia/".length() > 40)
					beginIndex = -1 ;
				endIndex += strLC.length() ;
				
				//	beginIndex = content.lastIndexOf("http://upload.wikimedia.org/wikipedia/commons/", endIndex) ;				
				
			}
			
			String link = "http:" + picDataStringContent.substring(beginIndex,endIndex) ;
			return link ;
			
		}catch( StringIndexOutOfBoundsException e){
//			System.out.print(fileName + ", " + str +'\n');
				e.printStackTrace() ;
		}		
		return null ;
	}
	
	private StorageWithRegistry picDataPageStorage ;
	private void initPicDataStorage()
	{
		if( picDataPageStorage == null )
			picDataPageStorage = new StorageWithRegistry( rootPath + PIC_DATA_SUBPATH, "pic-data" ) ;
		
		picDataPageStorage.initMap() ;	
	}

	
	private String getPicDataContent(URL url) throws IOException {
		
		initPicDataStorage();
		String content = picDataPageStorage.getContent( url.toExternalForm() ) ;
		if( content != null )
			return content ;
		
		BufferedInputStream iStream ;
		try{
			iStream = new BufferedInputStream( url.openStream() ) ;
		}
		catch(FileNotFoundException e){
			//e.printStackTrace() ;
			return null ;
		}
		byte[] byteArr = new byte[ iStream.available() + 1 ] ;
		
		int i = -1 ;
		int l = byteArr.length ;
		for ( int n = iStream.read() ; n != -1 ; n = iStream.read() )
		{
			if( i < l-1 )
				byteArr[ ++i ] = (byte)n ;
			else{
				int l0 = iStream.available() ;
				l0 = l0 > 0 ? l+l0 : (int)(l*1.2)+1 ;
				
				byte[] newByteArr = new byte[ l0 ] ;
				System.arraycopy( byteArr, 0, newByteArr, 0, l ) ;
				byteArr = newByteArr ;
				l = byteArr.length ;
			}
		}
		initPicDataStorage() ;
		
		content = new String( byteArr );
		picDataPageStorage.addContent( url.toString() , content) ;
		
		return content;
	}

	
	
	public static ArrayList<String> urlEncodingStrings = new ArrayList<String>() ;
	static{
		urlEncodingStrings.add("%") ; urlEncodingStrings.add("%25" ) ;
		
		urlEncodingStrings.add("!") ; urlEncodingStrings.add("%21" ) ;
		urlEncodingStrings.add("*") ; urlEncodingStrings.add("%2A" ) ;
		urlEncodingStrings.add("'") ; urlEncodingStrings.add("%27" ) ;
		urlEncodingStrings.add("(") ; urlEncodingStrings.add("%28" ) ;
		urlEncodingStrings.add(")") ; urlEncodingStrings.add("%29" ) ;
		
		urlEncodingStrings.add("$") ; urlEncodingStrings.add("%24" ) ;
		urlEncodingStrings.add("&") ; urlEncodingStrings.add("%26" ) ;
		urlEncodingStrings.add("+") ; urlEncodingStrings.add("%2B" ) ;
		urlEncodingStrings.add(",") ; urlEncodingStrings.add("%2C" ) ;
		urlEncodingStrings.add("/") ; urlEncodingStrings.add("%2F" ) ;
		urlEncodingStrings.add(":") ; urlEncodingStrings.add("%3A" ) ;
		urlEncodingStrings.add(";") ; urlEncodingStrings.add("%3B" ) ;
		urlEncodingStrings.add("=") ; urlEncodingStrings.add("%3D" ) ;
		urlEncodingStrings.add("?") ; urlEncodingStrings.add("%3F" ) ;
		urlEncodingStrings.add("@") ; urlEncodingStrings.add("%40" ) ;
		
		urlEncodingStrings.add("\""); urlEncodingStrings.add("%22" ) ;
		urlEncodingStrings.add("<") ; urlEncodingStrings.add("%3C" ) ;
		urlEncodingStrings.add(">") ; urlEncodingStrings.add("%3E" ) ;
		
		
		
		urlEncodingStrings.add("{") ; urlEncodingStrings.add("%7B" ) ;
		urlEncodingStrings.add("}") ; urlEncodingStrings.add("%7D" ) ;
		urlEncodingStrings.add("|") ; urlEncodingStrings.add("%7C" ) ;
		urlEncodingStrings.add("\\"); urlEncodingStrings.add("%5C" ) ;
		urlEncodingStrings.add("^") ; urlEncodingStrings.add("%5E" ) ;
		urlEncodingStrings.add("~") ; urlEncodingStrings.add("%7E" ) ;
		urlEncodingStrings.add("[") ; urlEncodingStrings.add("%5B" ) ;
		urlEncodingStrings.add("]") ; urlEncodingStrings.add("%5D" ) ;
		urlEncodingStrings.add("`") ; urlEncodingStrings.add("%60" ) ;
	}
	
	
	
	public static String performUrlEncoding( String str )
	{
		int s = urlEncodingStrings.size() ;
		for( int i = 0 ; i < s ; i+=2 )
			str = str.replace( urlEncodingStrings.get(i), urlEncodingStrings.get(i+1) ) ;
		
		return str ;
	}
	
	static boolean isPic( String str )
	{
		final String[] extArr = {"jpg", "jpeg", "JPG", "JPEG", "Jpeg", "Jpg", "png", "PNG", "gif", "GIF", "tif", "TIF", "tiff", "TIFF" } ;
		boolean result = false ;
		
		for( String s : extArr )
			result = result || str.endsWith( s ) ;
	
		return result ;
	}

	public void dispose() {
		this.picDataPageStorage = null ;
	}

}
